package com.viroyal.monitorlib.core;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.widget.Toast;

import com.hwangjr.rxbus.RxBus;
import com.suntiago.baseui.utils.log.Slog;
import com.viroyal.monitorlib.db.DBController;
import com.viroyal.monitorlib.entity.DownloadEntry;
import com.viroyal.monitorlib.notify.DataChanger;
import com.viroyal.monitorlib.utils.AppUtils;
import com.viroyal.monitorlib.utils.Constants;
import com.viroyal.monitorlib.utils.FileUtils;
import com.viroyal.monitorlib.utils.Trace;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;


/**
 * @author shuwoom
 * @email 294299195@qq.com
 * @date 2015-9-2
 * @update 2015-9-2
 * @des Service to manager download tasks.
 */
public class DownloadService extends Service {
    private static final String TAG = "DownloadService";
    public static final int NOTIFY_DOWNLOADING = 1;
    public static final int NOTIFY_UPDATING = 2;
    public static final int NOTIFY_PAUSED_OR_CANCELLED = 3;
    public static final int NOTIFY_COMPLETED = 4;
    public static final int NOTIFY_ERROR = 5;
    public static final int NOTIFY_CONNECTING = 6;
    public static final int NOTIFY_NOT_ENOUGH_SIZE = 7;

    private HashMap<String, DownloadTask> mDownloadingTasks;
    private LinkedBlockingQueue<DownloadEntry> mWaitingQueue;

    private ExecutorService mExecutor;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final DownloadEntry entry = (DownloadEntry) msg.obj;
            switch (msg.what) {
                case NOTIFY_COMPLETED://下载完成
                    if (mDownloadingTasks.containsKey(entry.url)) {
                        mDownloadingTasks.remove(entry.url);
                    }
                    File savePath = new File(DownloadConfig.DOWNLOAD_PATH);
                    if (!savePath.exists()) {
                        return;
                    }
                    File file = new File(savePath, entry.name);
                    RxBus.get().post(file);
                    String model= Build.MODEL;
                    String filePath = savePath+"/"+entry.name;
                    if(model.contains("JL005")){
                        String launcherActivityName = FileUtils.getLauncherActivityName(filePath,DownloadService.this);
                        if (TextUtils.isEmpty(launcherActivityName)){
                            Intent intent = new Intent("android.intent.action.SILENT_PACKAGE_INSTALL");
                            intent.putExtra("silent_install_file", filePath);
                            intent.putExtra("silent_install_auto_start", true);
                            intent.putExtra("silent_install_start_clazz", launcherActivityName);
                            sendBroadcast(intent);
                        }
                    }
//                    if (AppUtils.isAccessibilitySettingsOn(DownloadService.this)) {
//                        Slog.d(TAG, "handleMessage  [msg]: auto install");
                        install(file.getPath());
//                    } else {
//                        Slog.d(TAG, "handleMessage  [msg]: no auto install");
//                    }
                    break;
                case NOTIFY_UPDATING:
                    break;
                case NOTIFY_PAUSED_OR_CANCELLED:
                    checkNext(entry);
                    break;
                case NOTIFY_ERROR:
                    postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if (mDownloadingTasks.containsKey(entry.url)) {
                                mDownloadingTasks.remove(entry.url);
                            }
                            resumeDownload(entry);
                        }
                    }, 5000);
                    break;

                case NOTIFY_NOT_ENOUGH_SIZE:
                    Toast.makeText(getApplicationContext(), "存储卡空间不足，请清理！", Toast.LENGTH_SHORT).show();
                    checkNext(entry);
                    break;
            }
            DataChanger.getInstance(getApplication()).updateStatus(entry);
        }

    };

    private DataChanger dataChanger;
    private DBController dbController;

    public void install(String apkPath) {
        Slog.d(TAG, "install  [apkPath]:"+apkPath);
//        if (context != null) {
//            String apkPath = getApkPath(context);
        File file = new File(apkPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= 24) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //参数1：上下文，参数2：Provider主机地址和mainfest中配置的一致，参数3：共享的文件
            Uri contentUri = FileProvider.getUriForFile(this,
                    AppUtils.getPackageName(getApplicationContext()) + ".fileprovider", file);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            //不在activity中启动activity开启新的任务栈
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //1.Uri.parse("file://" + getApkPath(mContext))
            //2.Uri.fromFile(file)
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        }
        startActivity(intent);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mDownloadingTasks = new HashMap<>();
        mWaitingQueue = new LinkedBlockingQueue<>();

        mExecutor = Executors.newCachedThreadPool();
        dataChanger = DataChanger.getInstance(getApplicationContext());
        dbController = DBController.getInstance(getApplicationContext());
        intializeDownload();
        RxBus.get().register(this);

    }

    @Override
    public void onDestroy() {
        RxBus.get().unregister(this);
        super.onDestroy();
    }

    //防止App进程被强杀时数据丢失
    private void intializeDownload() {
        ArrayList<DownloadEntry> mDownloadEtries = dbController.queryAll();
        if (mDownloadEtries != null) {
            for (DownloadEntry entry : mDownloadEtries) {
                if (entry.status == DownloadEntry.DownloadStatus.downloading || entry.status == DownloadEntry.DownloadStatus.waiting) {
                    entry.status = DownloadEntry.DownloadStatus.pause;
                    if (DownloadConfig.getInstance().isRecoverDownloadWhenStart()) {
                        if (entry.isSupportRange) {
                            entry.status = DownloadEntry.DownloadStatus.pause;
                        } else {
                            entry.status = DownloadEntry.DownloadStatus.idle;
                            entry.reset();
                        }
                        addDownload(entry);
                    } else {
                        if (entry.isSupportRange) {
                            entry.status = DownloadEntry.DownloadStatus.pause;
                        } else {
                            entry.status = DownloadEntry.DownloadStatus.idle;
                            entry.reset();
                        }
                        dbController.newOrUpdate(entry);
                    }
                }
                dataChanger.addToOperatedEntryMap(entry.url, entry);
            }
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            int action = intent.getIntExtra(Constants.KEY_DOWNLOAD_ACTION, -1);
            DownloadEntry entry = (DownloadEntry) intent.getSerializableExtra(Constants.KEY_DOWNLOAD_ENTRY);
            /*****防止App进程被强杀时数据丢失*****/
            if (entry != null) {
                if (dataChanger.containsDownloadEntry(entry.url))
                    entry = dataChanger.queryDownloadEntryByUrl(entry.url);
                switch (action) {
                    case Constants.KEY_DOWNLOAD_ACTION_ADD:
                        addDownload(entry);
                        break;

                    case Constants.KEY_DOWNLOAD_ACTION_PAUSE:
                        pauseDownload(entry);
                        break;

                    case Constants.KEY_DOWNLOAD_ACTION_RESUME:
                        resumeDownload(entry);
                        break;

                    case Constants.KEY_DOWNLOAD_ACTION_CANCEL:
                        cancelDownload(entry);
                        break;

                    case Constants.KEY_DOWNLOAD_ACTION_PAUSE_ALL:
                        pauseAllDownload();
                        break;

                    case Constants.KEY_DOWNLOAD_ACTION_RECOVER_ALL:
                        recoverAllDownload();
                        break;

                    default:
                        break;

                }
            }
        }

        return super.onStartCommand(intent, flags, startId);
    }

    private void recoverAllDownload() {
        ArrayList<DownloadEntry> mRecoverableEntries = DataChanger.getInstance(getApplication()).queryAllRecoverableEntries();
        if (mRecoverableEntries == null) return;

        for (DownloadEntry entry : mRecoverableEntries) {
            addDownload(entry);
        }
        Trace.d("DownloadService==>recoverAllDownload" + "***Task Size:" + mDownloadingTasks.size() + "***Waiting Queue:" + mWaitingQueue.size());
    }

    private void pauseAllDownload() {
        while (mWaitingQueue.iterator().hasNext()) {
            DownloadEntry entry = mWaitingQueue.poll();
            entry.status = DownloadEntry.DownloadStatus.pause;
            DataChanger.getInstance(getApplication()).updateStatus(entry);
        }

        for (Map.Entry<String, DownloadTask> entry : mDownloadingTasks.entrySet()) {
            entry.getValue().pause();
        }
        mDownloadingTasks.clear();
        Trace.d("DownloadService==>pauseAllDownload");
    }

    private void checkNext(DownloadEntry entry) {
        mDownloadingTasks.remove(entry.url);
        DownloadEntry newEntry = mWaitingQueue.poll();
        if (newEntry != null) {
            startDownload(newEntry);
        }
    }

    private void cancelDownload(DownloadEntry entry) {
        DownloadTask task = mDownloadingTasks.remove(entry.url);
        if (task != null) {
            task.cancel();
            Trace.d("DownloadService==>pauseDownload#####cancel downloading task"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
        } else {
            mWaitingQueue.remove(entry);
            entry.status = DownloadEntry.DownloadStatus.cancel;
            DataChanger.getInstance(getApplication()).updateStatus(entry);
            Trace.d("DownloadService==>pauseDownload#####cancel waiting queue!"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
        }
    }

    private void resumeDownload(DownloadEntry entry) {
        addDownload(entry);
        Trace.d("DownloadService==>resumeDownload"
                + "***Task Size:" + mDownloadingTasks.size()
                + "***Waiting Queue:" + mWaitingQueue.size());
    }

    private void pauseDownload(DownloadEntry entry) {
        DownloadTask task = mDownloadingTasks.remove(entry.url);
        if (task != null) {
            Trace.d("DownloadService==>pauseDownload#####pause downloading task"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
            task.pause();
        } else {
            mWaitingQueue.remove(entry);
            entry.status = DownloadEntry.DownloadStatus.pause;
            DataChanger.getInstance(getApplication()).updateStatus(entry);
            Trace.d("DownloadService==>pauseDownload#####pause waiting queue!"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
        }

    }

    private void addDownload(DownloadEntry entry) {
        if (entry == null) {
            return;
        }
        checkDownloadPath(entry);
        if (isDownloadEntryRepeted(entry)) {
            return;
        }

        if (mDownloadingTasks.size() >= DownloadConfig.getInstance().getMax_download_tasks()) {
            mWaitingQueue.offer(entry);
            entry.status = DownloadEntry.DownloadStatus.waiting;
            DataChanger.getInstance(getApplication()).updateStatus(entry);
            Trace.d("DownloadService==>addDownload#####bigger than max_tasks"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
        } else {
            Trace.d("DownloadService==>addDownload#####start tasks"
                    + "***Task Size:" + mDownloadingTasks.size()
                    + "***Waiting Queue:" + mWaitingQueue.size());
            startDownload(entry);
        }
    }

    private void startDownload(DownloadEntry entry) {
        DownloadTask task = new DownloadTask(entry, mHandler, mExecutor);
        mDownloadingTasks.put(entry.url, task);
        Trace.d("DownloadService==>startDownload"
                + "***Task Size:" + mDownloadingTasks.size()
                + "***Waiting Queue:" + mWaitingQueue.size());
        task.start();
    }

    private void checkDownloadPath(DownloadEntry entry) {
        Trace.d("DownloadService==>checkDownloadPath()");
        if (entry == null) {
            return;
        }
        File file = new File(DownloadConfig.DOWNLOAD_PATH + entry.url.substring(entry.url.lastIndexOf("/") + 1));
        if (file == null) {
            Slog.d(TAG, "checkDownloadPath  [entry file == null]:");
            entry.reset();
        }
        if (file != null && !file.exists()) {
            Slog.d(TAG, "checkDownloadPath  [entry !file.exists()]:");
            entry.reset();
        }
    }

    private boolean isDownloadEntryRepeted(DownloadEntry entry) {
        if (mDownloadingTasks.get(entry.url) != null) {
            Trace.d("DownlaodService==>isDownloadEntryRepeted()##### The downloadEntry is in downloading tasks!!");
            return true;
        }

        if (mWaitingQueue.contains(entry)) {
            Trace.d("DownlaodService==>isDownloadEntryRepeted()##### The downloadEntry is in waiting queue!!");
            return true;
        }
        return false;
    }
}
